The data exploration stage focused on visualizing the relationships between variables and exploring patterns within the dataset.

The following sequence of commentary and code showcases the EDA that was conducted.

# Fine tune master_df before creating EDAs
master_df %>%
  mutate(
    date = ymd(date),
    weekday = factor(weekday,
                     levels = c("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday")),         season = factor(season, levels = c("Spring", "Summer", "Fall", "Winter")),
    hour = factor(hour, levels = 0:23),
    duration.min = round(Total.duration..ms. / 60000, digits = 2)
  ) %>%
  dplyr::select(X, Start.station, date, duration.min, End.station, Subscription.Type, CloudCover, Events, LATITUDE, LONGITUDE, Address, City, Zip, hour, weekday, weekend, rushhour, holiday, season, AdverseWeather, BeautifulWeather, weekend_holiday) -> map_df

The first thing we’ll do is to run distribution analysis on the main continuous variables in the dataset: total.rides and avg.duration. We will use levels of five categorical variables, i.e. Subscription.Type, weekend_holiday, rushhour, season, and AdverseWeather, as group coloring to generate high level between-group distribution comparison.

map_df %>%
  group_by(date, hour, Subscription.Type) %>%
  summarise(
    total.rides = n(),
    avg.duration = mean(duration.min), 
    weekend_holiday = first(weekend_holiday),
    weekday = first(weekday),
    rushhour = first(rushhour),
    season = first(season),
    AdverseWeather = first(AdverseWeather)
  ) -> day_hour_rides
g1 + geom_histogram(mapping = aes(total.rides, fill = as.factor(AdverseWeather)), binwidth = 0.5) -> g10
There were 24 warnings (use warnings() to see them)
g1 + geom_histogram(mapping = aes(log(day_hour_rides$avg.duration),
                                  fill = as.factor(AdverseWeather)), bins = 100) -> g11
# Display the histograms
plot_grid(g2, g3, nrow = 2, rel_widths = c(1/2, 1/2))

plot_grid(g4, g5, nrow = 2, rel_widths = c(1/2, 1/2))

plot_grid(g6, g7, nrow = 2, rel_widths = c(1/2, 1/2))

plot_grid(g8, g9, nrow = 2, rel_widths = c(1/2, 1/2))

plot_grid(g10, g11, nrow = 2, rel_widths = c(1/2, 1/2))

Our first impression is that the distribution of total.rides is skewing right, while the distribution of avg.duration has two modes.

More specifically, the avg.duration distribution by Subscription.Type graph indicates that registered bikers are contributing to the lower duration mode while the casual bikers are contrbution to the higher mode. Casual bikers have much less total.rides than the registered bikers. In the distribution by rushhour graph, commuting hour rides are dominating hours that have higher count of total.rides. Rushhour rides are also contributing more to the lower avg.duration mode. Another interesting finding from the distribution by season graph is that winter has much more short-duration rides than other seasons, while spring and summer have more long-duration rides among casual riders.

The above analysis indicates that time-related factors are having a strong impact on the dependent variables. In our next step, we will create heatmaps for hour of the day / day of the week to futher explore the patterns.

# Create a subset just for the time heatmap
day_hour_rides %>%
  ungroup() %>%
  select(hour, weekday, total.rides, avg.duration) %>%
  mutate(total_duration = total.rides * avg.duration, 
         hour = factor(hour, levels = (0:23))) %>%
  group_by(hour, weekday) %>%
  summarise(count.rides = sum(total.rides), total.duration = sum(total_duration)) -> df.1
# Create time based heatmaps
g10 <- ggplot(data=df.1, aes(x=hour, y=weekday, fill=count.rides)) +
  geom_tile(color="white", size=0.1)+ coord_equal() +
  labs(x=NULL, y=NULL, title="Count of Rides Per Weekday & Hour of Day") +
  theme_tufte(base_family="Calibri") + theme(plot.title=element_text(hjust=0.5, size = 10)) +
  theme(axis.ticks=element_blank()) + theme(axis.text=element_text(size=7)) + theme(legend.position="none") +
  scale_fill_gradient(low = "white", high = "steelblue")
g11 <- ggplot(data=df.1, aes(x=hour, y=weekday, fill=total.duration)) +
  geom_tile(color="white", size=0.1)+ coord_equal() +
  labs(x=NULL, y=NULL, title="Total Duration Per Weekday & Hour of Day") +
  theme_tufte(base_family="Calibri") + theme(plot.title=element_text(hjust=0.5, size = 10)) + theme(legend.position="none") +
  theme(axis.ticks=element_blank()) + theme(axis.text=element_text(size=7)) +
  scale_fill_gradient(low = "white", high = "firebrick")
g12 <- ggplot(data=df.1, aes(x=hour, y=weekday, fill=total.duration/count.rides)) +
  geom_tile(color="white", size=0.1)+ coord_equal() +
  labs(x=NULL, y=NULL, title="Average Duration Per Weekday & Hour of Day") +
  theme_tufte(base_family="Calibri") + theme(plot.title=element_text(hjust=0.5, size = 10)) + theme(legend.position="none") +
  theme(axis.ticks=element_blank()) + theme(axis.text=element_text(size=7)) +
  scale_fill_gradient(low = "white", high = "springgreen3")
plot_grid(g10, g12, nrow = 2, rel_heights = c(1/2, 1/2))

Here we find some interesting patterns from the hour-weekday heatmap. It seems that more rides have taken place during rush hours on work days, while total.rides distributes evenly in day time on weekend. The avg.duration of the rides appears to be longer during day time over the weekend.

After we have a general understanding of the data, we move on to explore the geospatial distribution of total.rides across the DC metro area. First let us plot the bike stations.

# Create station list with coordinates, total count of rides, and total duration of rides
map.stations <- map_df %>%
  group_by(Start.station) %>%
  summarise(total.rides = n(),
            avg.duration = mean(duration.min),
            subscriber.percentage = mean(Subscription.Type == "Registered"),
            lat = first(LATITUDE),
            lon = first(LONGITUDE)
            )
head(map.stations)

Below we can see the locations of all the bike share stations across the DMV area, with the circle size representing total.rides and color representing avg.rides. It appears that bike stations are spreading out well in the DMV area, with stations located in DMV ourskirts such as Alexandria, VA, Bethesda, MD, and Silver Spring, MD.

# download basic map layers for plotting
base.map <- qmap("Wasington DC", zoom = 12, source= "google", maptype="roadmap", color = "bw", crop=FALSE, legend='topleft')
base.map.1 <- qmap("Wasington DC", zoom = 13, source= "google", maptype="roadmap", color = "bw", crop=FALSE, legend='topleft')
base.map.2 <- qmap("Wasington DC", zoom = 14, source= "google", maptype="roadmap", color = "bw", crop=FALSE, legend='topleft')
base.map + geom_point(aes(x = lon, y = lat, size=total.rides, color=avg.duration), data = map.stations,
 alpha = .5)+ scale_size(range = c(1, 5)) + scale_colour_gradient(low = "purple", high = "red")

base.map.1 + geom_point(aes(x = lon, y = lat, size=total.rides, color=avg.duration), data = map.stations,
 alpha = .5) + scale_size(range = c(1, 5)) + scale_colour_gradient(low = "purple", high = "red")

base.map.2 + geom_point(aes(x = lon, y = lat, size=total.rides, color=avg.duration), data = map.stations,
 alpha = .5) + scale_size(range = c(1, 10)) + scale_colour_gradient(low = "purple", high = "red")

But how does the actual count of total.rides distribute across the area? Will it go in line with the bike station locations? We then move on to create a heatmap based on the density of total.rides on the map. The graph below indicates that total.rides are way more condensed than the distribution of the bike stations, with the most rides happening in the DC heart area, such as Dupont Circle, Logan Circle, National Mall, Metro Center, Gallery Place, World Bank, and Lincoln Memorial.

# Create a ride data set with location and ride, will also keep sliceability with other factors
# Adjust factor level names for better display in faceted visuals
map_df %>%
  mutate(lon = LONGITUDE, lat = LATITUDE) %>%
  select(X, Subscription.Type, Events, lat, lon, hour,
         weekday, weekend, rushhour, holiday, season, AdverseWeather, BeautifulWeather, weekend_holiday) %>%
  mutate(
    hour = as.numeric(hour),
    AdverseWeather = as.factor(if_else(AdverseWeather=="True", "Adverse: Yes", "Adverse: No")),
    BeautifulWeather = as.factor(if_else(BeautifulWeather == "True", "Beautiful: Yes", "Beautiful: No")),
    holiday = as.factor(if_else(holiday == "1", "Holiday: Yes", "Holiday: No")),
    weekend = as.factor(if_else(weekend == "1", "Weekend: Yes", "Weekend: No")),
    rushhour = as.factor(if_else(rushhour == "1", "Rush Hour: Yes", "Rush Hour: No")),
    weekend_holiday = as.factor(if_else(weekend_holiday == "1", "Leisure Day: Yes", "Leisure Day: No")),
    time_of_day = factor(if_else(hour>4 & hour < 13, "Morning",
                                    if_else(hour>12 & hour < 19, "Afternoon", 
                                            if_else(hour >16 & hour <= 23, "Night", "Late Night"))),
                            levels = c("Morning", "Afternoon", "Night", "Late Night")),
    hour = factor(hour, levels = 0:23)) -> ride_df
# Create ride density maps
base.map + geom_density2d(data = ride_df[sample(1:nrow(ride_df), 10000),], 
    aes(x = lon, y = lat), size = 0.4) + stat_density2d(data = ride_df[sample(1:nrow(ride_df), 10000),], 
    aes(x = lon, y = lat, fill = ..level.., alpha = ..level..), size = 1, 
    bins = 5, geom = "polygon", contour = TRUE) + scale_fill_gradient(low = "springgreen", high = "red") + 
    scale_alpha(range = c(0, 0.3), guide = FALSE)

base.map.1 + geom_density2d(data = ride_df[sample(1:nrow(ride_df), 10000),], 
    aes(x = lon, y = lat), size = 0.4) + stat_density2d(data = ride_df[sample(1:nrow(ride_df), 10000),], 
    aes(x = lon, y = lat, fill = ..level.., alpha = ..level..), size = 2, 
    bins = 8, geom = "polygon", contour = TRUE) + scale_fill_gradient(low = "springgreen", high = "red") + 
    scale_alpha(range = c(0, 0.3), guide = FALSE)

base.map.2 + geom_density2d(data = ride_df[sample(1:nrow(ride_df), 10000),], 
    aes(x = lon, y = lat), size = 0.5) + stat_density2d(data = ride_df[sample(1:nrow(ride_df), 10000),], 
    aes(x = lon, y = lat, fill = ..level.., alpha = ..level..), size = 3, 
    bins = 15, geom = "polygon", contour = TRUE) + scale_fill_gradient(low = "springgreen", high = "red") + 
    scale_alpha(range = c(0, 0.3), guide = FALSE)

Since we now have a general idea of where the most rides are happening in DC, our next step is to slice the ridership data with factors we generated from time and weather and compare the patterns. We wanted to see if the popularity of the stations changed under different time and weather conditions.

# Create a subsliced ridership set of 15000 observations
ride_df.sample <- ride_df[sample(1:nrow(ride_df), 15000),]

Our next step is to slice the ridership data accorindg to factors we generated from weather and time. We wanted to see if the popularity of the stations changed under different time and weather conditions.

# Ride frequency heatmap by seasons
dc.map.3 + stat_density2d(aes(x=lon, y=lat, fill=..level.., alpha=..level..),
                          bins=7, geom="polygon", data=ride_df.sample) +
  scale_fill_gradient(low="springgreen", high="tomato") + scale_alpha(range = c(0.1, 0.6), guide = FALSE) + 
  facet_wrap(~season, nrow = 1) +
  guides(fill=guide_legend(title="ride\nfrequency")) +
  ggtitle("Ride Distribution by Seasons") +
  theme(axis.title=element_blank(),
        axis.text=element_blank(),
        axis.ticks=element_blank(),
        legend.text = element_blank(),
        plot.title = element_text(color="black", size=16, hjust=0)) -> g13
g13

The first graph shows the distribution of rides in each season of the year of 2015. In Spring and Summer, both Lincoln Memorial and National Mall enjoy more rides from other time of the year. During winter, however, it seems that more people are taking bike rides around Logan Circle, Foggy Bottom, and Metro Center, i.e. the inner center of the District.

# Ride frequency heatmap by time of day
dc.map.3 + stat_density2d(aes(x=lon, y=lat, fill=..level.., alpha=..level..),
                          bins=7, geom="polygon", data=ride_df.sample) +
  scale_fill_gradient(low="springgreen", high="tomato") + scale_alpha(range = c(0.1, 0.6), guide = FALSE) + 
  facet_wrap(~time_of_day, nrow = 1) +
  guides(fill=guide_legend(title="ride\nfrequency")) +
  ggtitle("Ride Distribution by Time of Day") +
  theme(axis.title=element_blank(),
        axis.text=element_blank(),
        axis.ticks=element_blank(),
        legend.text = element_blank(),
        plot.title = element_text(color="black", size=16, hjust=0)) -> g14
g14

Another similar comparison based on time of the day shows that people are taking more rides in central to northeastern DC in the morning and more in central to southwestern DC in the afternoon. Bikers start their rides mostly around DuPont circle, Logan Circle, Metro Center, and Gallery Place at night. Few people will start their rides in late night, of course; but we are seeing relatively more rides in the central to northwestern DC area. It seems that people’s daily routine is contributing to this pattern, considering that these areas correspond to the residence area, working area, and entertaining/event area in DC.

# Ride frequency heatmap by rush hour
dc.map.3 + stat_density2d(aes(x=lon, y=lat, fill=..level.., alpha=..level..),
                          bins=7, geom="polygon", data=ride_df.sample) +
  scale_fill_gradient(low="springgreen", high="tomato") + scale_alpha(range = c(0.1, 0.6), guide = FALSE) + 
  facet_wrap(~rushhour) +
  guides(fill=guide_legend(title="ride\nfrequency")) +
  ggtitle("Ride Distribution - Rush Hour?") +
  theme(axis.title=element_blank(),
        axis.text=element_blank(),
        axis.ticks=element_blank(),
        legend.text = element_blank(),
        plot.title = element_text(color="black", size=16, hjust=0)) -> g15
g15

Since time is creating interesting impact on total.rides and bikes can be a useful tool for commuting, we want to check out specifically the allocation of rides for rush hours againt other time of the day. In the above graph, we notice that more people are taking bike rides near Metro Center, Gallery Place, and Capital Hill during rush hours, while more people are taking rides near Lincoln Memorial and National Mall during non-rush hours. This information is interesting, since Metro center, Gallery place, and Capital Hill are places where many people go to work, while (apparently) Lincoln Memorial and National Mall are popular tourist sites.

# Ride frequency heatmap by weekend/holiday
dc.map.3 + stat_density2d(aes(x=lon, y=lat, fill=..level.., alpha=..level..),
                          bins=7, geom="polygon", data=ride_df.sample) +
  scale_fill_gradient(low="springgreen", high="tomato") + scale_alpha(range = c(0.1, 0.6), guide = FALSE) + 
  facet_wrap(~weekend_holiday + BeautifulWeather, nrow = 1) +
  guides(fill=guide_legend(title="ride\nfrequency")) +
  ggtitle("Ride Distribution - Leisure Days X Good Weather") +
  theme(axis.title=element_blank(),
        axis.text=element_blank(),
        axis.ticks=element_blank(),
        legend.text = element_blank(),
        plot.title = element_text(color="black", size=16, hjust=0)) -> g16
g16

Since Lincoln Memorial and National Mall are enjoying much love in non-rush hours, we are interested to check out if leisure time will have a different pattern for total.rides distribution. Comparing the left two graphs in the above chart, it is apparent that the distribution of ridership is sparse for leisure days in good weather: riders are of course starting their rides from many different stations across the District. Interestingly, the second left graph shows that bikers mostly still ride in the central DC during working days despite the good weather. Commuting really seems to be a major function of the shared bikes!

Since commuting seems to be a really big factor for the distribution of rides, we are insterested to dig a bit deeper into the type of subscription for each ride. Since bike share subscribers are more likely to use bikes for commute, will we see a clear difference between casual and registered bikers?

# Ride frequency heatmap by Subscription Type
dc.map.2 + stat_density2d(aes(x=lon, y=lat, fill=..level.., alpha=..level..),
                          bins=7, geom="polygon", data=ride_df.sample) +
  scale_fill_gradient(low="springgreen", high="tomato") + scale_alpha(range = c(0.1, 0.6), guide = FALSE) + 
  facet_wrap(~Subscription.Type + rushhour, nrow = 1) +
  guides(fill=guide_legend(title="ride\nfrequency")) +
  ggtitle("Ride Distribution by Subscription Type & Rush Hour") +
  theme(axis.title=element_blank(),
        axis.text=element_blank(),
        axis.ticks=element_blank(),
        legend.text = element_blank(),
        plot.title = element_text(color="black", size=16, hjust=0)) -> g17
g17

The above graph shows that casual bikers are (apparently) taking more rides around the tourist attraction sites in DC, no matter if it’s in rush hour or not. For the subscribers, however, the distribution of rides are surprisingly even no matter it’s rush hour or not. If we really consider the nature of commuting, this actually makes sense: for people that ride bikes based on their daily commuting needs, they will need to use bikes to get to work or go home. The green area in the right two graphs actually shows the routine start stations for the registered users!

# Ride frequency heatmap by adverse weather
dc.map.2 + stat_density2d(aes(x=lon, y=lat, fill=..level.., alpha=..level..),
                          bins=7, geom="polygon", data=ride_df.sample) +
  scale_fill_gradient(low="springgreen", high="tomato") + scale_alpha(range = c(0.1, 0.6), guide = FALSE) + 
  facet_wrap(~AdverseWeather) +
  guides(fill=guide_legend(title="ride\nfrequency")) +
  ggtitle("Ride Distribution - Adverse Weather?") +
  theme(axis.title=element_blank(),
        axis.text=element_blank(),
        axis.ticks=element_blank(),
        legend.text = element_blank(),
        plot.title = element_text(color="black", size=16, hjust=0)) -> g18
g18

A quick comparison of adverse weather against non-adverse weather shows not much difference for the ridership. This might be due to the nature of our integrated weather data: the weather information is the mean values for a whole day, thus making it hard for the slicers to differentiate ridership distribution on a lower grain level.

# Ride frequency heatmap by rush hour and adverse weather
dc.map.3 + stat_density2d(aes(x=lon, y=lat, fill=..level.., alpha=..level..),
                          bins=7, geom="polygon", data=ride_df.sample) +
  scale_fill_gradient(low="springgreen", high="tomato") + scale_alpha(range = c(0.1, 0.6), guide = FALSE) + 
  facet_wrap(~AdverseWeather + rushhour, nrow = 1) +
  guides(fill=guide_legend(title="ride\nfrequency")) +
  ggtitle("Ride Distribution - Bad Weather X Rush Hour") +
  theme(axis.title=element_blank(),
        axis.text=element_blank(),
        axis.ticks=element_blank(),
        legend.text = element_blank(),
        plot.title = element_text(color="black", size=16, hjust=0)) -> g19
g19

Again, in the graph shown above here, we observe a bigger differece from Rush Hour than the weather. This seems to be related to the same challenge we are having from the weather variables.

LS0tDQp0aXRsZTogIkV4cGxvcmF0b3J5IERhdGEgQW5hbHl0aWNzIG9uIENhcGl0YWwgQmlrZXNoYXJlIERhdGEgMjAxNSINCmF1dGhvcjogIkVsdmluIE91eWFuZyINCmRhdGU6ICJEZWNlbWJlciA1LCAyMDE2Ig0Kb3V0cHV0OiANCiAgaHRtbF9ub3RlYm9vazogDQogICAgZmlnX2hlaWdodDogOA0KICAgIGZpZ193aWR0aDogOA0KLS0tDQoNCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQpgYGANCg0KVGhlIGRhdGEgZXhwbG9yYXRpb24gc3RhZ2UgZm9jdXNlZCBvbiB2aXN1YWxpemluZyB0aGUgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHZhcmlhYmxlcyBhbmQgZXhwbG9yaW5nICBwYXR0ZXJucyB3aXRoaW4gdGhlIGRhdGFzZXQuIA0KDQpUaGUgZm9sbG93aW5nIHNlcXVlbmNlIG9mIGNvbW1lbnRhcnkgYW5kIGNvZGUgc2hvd2Nhc2VzIHRoZSBFREEgdGhhdCB3YXMgY29uZHVjdGVkLg0KDQoNCmBgYHtyLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeShwbHlyKQ0KbGlicmFyeShycGFydCkgIyBSdW4gUmVncmVzc2lvbiBUcmVlIG1vZGVsDQpsaWJyYXJ5KHJwYXJ0LnBsb3QpICMgUGxvdCB0cmVlIGF0IFJlZ3Jlc3Npb24gVHJlZSBtb2RlbA0KbGlicmFyeShyYW5kb21Gb3Jlc3QpICMgUnVuIFJhbmRvbSBGb3Jlc3QgbW9kZWwNCmxpYnJhcnkocFJPQykgIyBQbG90IFJPQyBjdXJ2ZSBhdCBSYW5kb20gRm9yZXN0IG1vZGVsDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGdyaWRFeHRyYSkNCmxpYnJhcnkocmVzaGFwZTIpICMgRm9yIG1lbHQgZnVuY3Rpb24NCmxpYnJhcnkoTUFTUykNCmxpYnJhcnkoZ2dtYXApICMgRXh0cmFjdGluZyBhZGRyZXNzIGluZm9ybWF0aW9uIGZyb20gZ3BzIGluZm8NCmxpYnJhcnkoc3RyaW5ncikgIyBFeHRyYWN0aW5nIHppcGNvZGVzIGZyb20gZ3BzIGluZm8NCmxpYnJhcnkoY2FyZXQpICMgRm9yIGRhdGEgcGFydGl0aW9uDQpsaWJyYXJ5KHBsb3RseSkgIyBGb3IgaW50ZXJhY3RpdmUgbWFwcGluZyBhbmQgdmlzdWFscw0KbGlicmFyeShsZWFmbGV0KQ0KbGlicmFyeShjb3dwbG90KQ0KbGlicmFyeShnZ3RoZW1lcykNCmxpYnJhcnkodmlyaWRpcykNCmxpYnJhcnkoa25pdHIpDQpsaWJyYXJ5KHNjYWxlcykNCmBgYA0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCm1hc3Rlcl9kZjwtcmVhZC5jc3YoJ21hc3Rlcl9kZi5jc3YnKQ0Kc3RyKG1hc3Rlcl9kZikNCmBgYA0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCiMgRmFjdG9yaXplIHZhcmlhYmxlcw0KbWFzdGVyX2RmJENsb3VkQ292ZXI8LSBhcy5mYWN0b3IobWFzdGVyX2RmJENsb3VkQ292ZXIpDQptYXN0ZXJfZGYkWmlwPC0gYXMuZmFjdG9yKG1hc3Rlcl9kZiRaaXApDQptYXN0ZXJfZGYkaG91cjwtIGFzLmZhY3RvcihtYXN0ZXJfZGYkaG91cikNCm1hc3Rlcl9kZiR3ZWVrZW5kPC0gYXMuZmFjdG9yKG1hc3Rlcl9kZiR3ZWVrZW5kKQ0KbWFzdGVyX2RmJHJ1c2hob3VyPC0gYXMuZmFjdG9yKG1hc3Rlcl9kZiRydXNoaG91cikNCm1hc3Rlcl9kZiRob2xpZGF5PC0gYXMuZmFjdG9yKG1hc3Rlcl9kZiRob2xpZGF5KQ0KbWFzdGVyX2RmJHdlZWtlbmRfaG9saWRheTwtIGFzLmZhY3RvcihtYXN0ZXJfZGYkd2Vla2VuZF9ob2xpZGF5KQ0KYGBgDQpgYGB7cn0NCiMgRmluZSB0dW5lIG1hc3Rlcl9kZiBiZWZvcmUgY3JlYXRpbmcgRURBcw0KbWFzdGVyX2RmICU+JQ0KICBtdXRhdGUoDQogICAgZGF0ZSA9IHltZChkYXRlKSwNCiAgICB3ZWVrZGF5ID0gZmFjdG9yKHdlZWtkYXksDQogICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJTdW5kYXkiLCAiTW9uZGF5IiwgIlR1ZXNkYXkiLCAiV2VkbmVzZGF5IiwgIlRodXJzZGF5IiwgIkZyaWRheSIsICJTYXR1cmRheSIpKSwNCiAgICBzZWFzb24gPSBmYWN0b3Ioc2Vhc29uLCBsZXZlbHMgPSBjKCJTcHJpbmciLCAiU3VtbWVyIiwgIkZhbGwiLCAiV2ludGVyIikpLA0KICAgIGhvdXIgPSBmYWN0b3IoaG91ciwgbGV2ZWxzID0gMDoyMyksDQogICAgZHVyYXRpb24ubWluID0gcm91bmQoVG90YWwuZHVyYXRpb24uLm1zLiAvIDYwMDAwLCBkaWdpdHMgPSAyKQ0KICApICU+JQ0KICBkcGx5cjo6c2VsZWN0KFgsIFN0YXJ0LnN0YXRpb24sIGRhdGUsIGR1cmF0aW9uLm1pbiwgRW5kLnN0YXRpb24sIFN1YnNjcmlwdGlvbi5UeXBlLCBDbG91ZENvdmVyLCBFdmVudHMsIExBVElUVURFLCBMT05HSVRVREUsIEFkZHJlc3MsIENpdHksIFppcCwgaG91ciwgd2Vla2RheSwgd2Vla2VuZCwgcnVzaGhvdXIsIGhvbGlkYXksIHNlYXNvbiwgQWR2ZXJzZVdlYXRoZXIsIEJlYXV0aWZ1bFdlYXRoZXIsIHdlZWtlbmRfaG9saWRheSkgLT4gbWFwX2RmDQpgYGANClRoZSBmaXJzdCB0aGluZyB3ZSdsbCBkbyBpcyB0byBydW4gZGlzdHJpYnV0aW9uIGFuYWx5c2lzIG9uIHRoZSBtYWluIGNvbnRpbnVvdXMgdmFyaWFibGVzIGluIHRoZSBkYXRhc2V0OiB0b3RhbC5yaWRlcyBhbmQgYXZnLmR1cmF0aW9uLiBXZSB3aWxsIHVzZSBsZXZlbHMgb2YgZml2ZSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMsIGkuZS4gU3Vic2NyaXB0aW9uLlR5cGUsIHdlZWtlbmRfaG9saWRheSwgcnVzaGhvdXIsIHNlYXNvbiwgYW5kIEFkdmVyc2VXZWF0aGVyLCBhcyBncm91cCBjb2xvcmluZyB0byBnZW5lcmF0ZSBoaWdoIGxldmVsIGJldHdlZW4tZ3JvdXAgZGlzdHJpYnV0aW9uIGNvbXBhcmlzb24uDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbWFwX2RmICU+JQ0KICBncm91cF9ieShkYXRlLCBob3VyLCBTdWJzY3JpcHRpb24uVHlwZSkgJT4lDQogIHN1bW1hcmlzZSgNCiAgICB0b3RhbC5yaWRlcyA9IG4oKSwNCiAgICBhdmcuZHVyYXRpb24gPSBtZWFuKGR1cmF0aW9uLm1pbiksIA0KICAgIHdlZWtlbmRfaG9saWRheSA9IGZpcnN0KHdlZWtlbmRfaG9saWRheSksDQogICAgd2Vla2RheSA9IGZpcnN0KHdlZWtkYXkpLA0KICAgIHJ1c2hob3VyID0gZmlyc3QocnVzaGhvdXIpLA0KICAgIHNlYXNvbiA9IGZpcnN0KHNlYXNvbiksDQogICAgQWR2ZXJzZVdlYXRoZXIgPSBmaXJzdChBZHZlcnNlV2VhdGhlcikNCiAgKSAtPiBkYXlfaG91cl9yaWRlcw0KYGBgDQpgYGB7cn0NCiMgQ3JlYXRlIGRpc3RyaWJ1dGlvbiBoaXN0b2dyYW1zDQpnMSA8LSBnZ3Bsb3QoZGF0YT1kYXlfaG91cl9yaWRlcykNCmcxICsgZ2VvbV9oaXN0b2dyYW0obWFwcGluZyA9IGFlcyh0b3RhbC5yaWRlcywgZmlsbCA9IFN1YnNjcmlwdGlvbi5UeXBlKSwgYmlud2lkdGggPSAwLjUpIC0+IGcyDQpnMSArIGdlb21faGlzdG9ncmFtKG1hcHBpbmcgPSBhZXMobG9nKGRheV9ob3VyX3JpZGVzJGF2Zy5kdXJhdGlvbiksIGZpbGwgPSBTdWJzY3JpcHRpb24uVHlwZSksIGJpbnMgPSAxMDApIC0+IGczDQpnMSArIGdlb21faGlzdG9ncmFtKG1hcHBpbmcgPSBhZXModG90YWwucmlkZXMsIGZpbGwgPSB3ZWVrZW5kX2hvbGlkYXkpLCBiaW53aWR0aCA9IDAuNSkgLT4gZzQNCmcxICsgZ2VvbV9oaXN0b2dyYW0obWFwcGluZyA9IGFlcyhsb2coZGF5X2hvdXJfcmlkZXMkYXZnLmR1cmF0aW9uKSwgZmlsbCA9IHdlZWtlbmRfaG9saWRheSksIGJpbnMgPSAxMDApIC0+IGc1DQpnMSArIGdlb21faGlzdG9ncmFtKG1hcHBpbmcgPSBhZXModG90YWwucmlkZXMsIGZpbGwgPSBhcy5mYWN0b3IocnVzaGhvdXIpKSwgYmlud2lkdGggPSAwLjUpIC0+IGc2DQpnMSArIGdlb21faGlzdG9ncmFtKG1hcHBpbmcgPSBhZXMobG9nKGRheV9ob3VyX3JpZGVzJGF2Zy5kdXJhdGlvbiksIGZpbGwgPSBhcy5mYWN0b3IocnVzaGhvdXIpKSwgYmlucyA9IDEwMCkgLT4gZzcNCmcxICsgZ2VvbV9oaXN0b2dyYW0obWFwcGluZyA9IGFlcyh0b3RhbC5yaWRlcywgZmlsbCA9IGFzLmZhY3RvcihzZWFzb24pKSwgYmlud2lkdGggPSAwLjUpIC0+IGc4DQpnMSArIGdlb21faGlzdG9ncmFtKG1hcHBpbmcgPSBhZXMobG9nKGRheV9ob3VyX3JpZGVzJGF2Zy5kdXJhdGlvbiksIGZpbGwgPSBhcy5mYWN0b3Ioc2Vhc29uKSksIGJpbnMgPSAxMDApIC0+IGc5DQpnMSArIGdlb21faGlzdG9ncmFtKG1hcHBpbmcgPSBhZXModG90YWwucmlkZXMsIGZpbGwgPSBhcy5mYWN0b3IoQWR2ZXJzZVdlYXRoZXIpKSwgYmlud2lkdGggPSAwLjUpIC0+IGcxMA0KZzEgKyBnZW9tX2hpc3RvZ3JhbShtYXBwaW5nID0gYWVzKGxvZyhkYXlfaG91cl9yaWRlcyRhdmcuZHVyYXRpb24pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBhcy5mYWN0b3IoQWR2ZXJzZVdlYXRoZXIpKSwgYmlucyA9IDEwMCkgLT4gZzExDQpgYGANCmBgYHtyfQ0KIyBEaXNwbGF5IHRoZSBoaXN0b2dyYW1zDQpwbG90X2dyaWQoZzIsIGczLCBucm93ID0gMiwgcmVsX3dpZHRocyA9IGMoMS8yLCAxLzIpKQ0KcGxvdF9ncmlkKGc0LCBnNSwgbnJvdyA9IDIsIHJlbF93aWR0aHMgPSBjKDEvMiwgMS8yKSkNCnBsb3RfZ3JpZChnNiwgZzcsIG5yb3cgPSAyLCByZWxfd2lkdGhzID0gYygxLzIsIDEvMikpDQpwbG90X2dyaWQoZzgsIGc5LCBucm93ID0gMiwgcmVsX3dpZHRocyA9IGMoMS8yLCAxLzIpKQ0KcGxvdF9ncmlkKGcxMCwgZzExLCBucm93ID0gMiwgcmVsX3dpZHRocyA9IGMoMS8yLCAxLzIpKQ0KYGBgDQpPdXIgZmlyc3QgaW1wcmVzc2lvbiBpcyB0aGF0IHRoZSBkaXN0cmlidXRpb24gb2YgdG90YWwucmlkZXMgaXMgc2tld2luZyByaWdodCwgd2hpbGUgdGhlIGRpc3RyaWJ1dGlvbiBvZiBhdmcuZHVyYXRpb24gaGFzIHR3byBtb2Rlcy4NCg0KTW9yZSBzcGVjaWZpY2FsbHksIHRoZSBhdmcuZHVyYXRpb24gZGlzdHJpYnV0aW9uIGJ5IFN1YnNjcmlwdGlvbi5UeXBlIGdyYXBoIGluZGljYXRlcyB0aGF0IHJlZ2lzdGVyZWQgYmlrZXJzIGFyZSBjb250cmlidXRpbmcgdG8gdGhlIGxvd2VyIGR1cmF0aW9uIG1vZGUgd2hpbGUgdGhlIGNhc3VhbCBiaWtlcnMgYXJlIGNvbnRyYnV0aW9uIHRvIHRoZSBoaWdoZXIgbW9kZS4gQ2FzdWFsIGJpa2VycyBoYXZlIG11Y2ggbGVzcyB0b3RhbC5yaWRlcyB0aGFuIHRoZSByZWdpc3RlcmVkIGJpa2Vycy4gSW4gdGhlIGRpc3RyaWJ1dGlvbiBieSBydXNoaG91ciBncmFwaCwgY29tbXV0aW5nIGhvdXIgcmlkZXMgYXJlIGRvbWluYXRpbmcgaG91cnMgdGhhdCBoYXZlIGhpZ2hlciBjb3VudCBvZiB0b3RhbC5yaWRlcy4gUnVzaGhvdXIgcmlkZXMgYXJlIGFsc28gY29udHJpYnV0aW5nIG1vcmUgdG8gdGhlIGxvd2VyIGF2Zy5kdXJhdGlvbiBtb2RlLiBBbm90aGVyIGludGVyZXN0aW5nIGZpbmRpbmcgZnJvbSB0aGUgZGlzdHJpYnV0aW9uIGJ5IHNlYXNvbiBncmFwaCBpcyB0aGF0IHdpbnRlciBoYXMgbXVjaCBtb3JlIHNob3J0LWR1cmF0aW9uIHJpZGVzIHRoYW4gb3RoZXIgc2Vhc29ucywgd2hpbGUgc3ByaW5nIGFuZCBzdW1tZXIgaGF2ZSBtb3JlIGxvbmctZHVyYXRpb24gcmlkZXMgYW1vbmcgY2FzdWFsIHJpZGVycy4NCg0KVGhlIGFib3ZlIGFuYWx5c2lzIGluZGljYXRlcyB0aGF0IHRpbWUtcmVsYXRlZCBmYWN0b3JzIGFyZSBoYXZpbmcgYSBzdHJvbmcgaW1wYWN0IG9uIHRoZSBkZXBlbmRlbnQgdmFyaWFibGVzLiBJbiBvdXIgbmV4dCBzdGVwLCB3ZSB3aWxsIGNyZWF0ZSBoZWF0bWFwcyBmb3IgaG91ciBvZiB0aGUgZGF5IC8gZGF5IG9mIHRoZSB3ZWVrIHRvIGZ1dGhlciBleHBsb3JlIHRoZSBwYXR0ZXJucy4NCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIENyZWF0ZSBhIHN1YnNldCBqdXN0IGZvciB0aGUgdGltZSBoZWF0bWFwDQpkYXlfaG91cl9yaWRlcyAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICBzZWxlY3QoaG91ciwgd2Vla2RheSwgdG90YWwucmlkZXMsIGF2Zy5kdXJhdGlvbikgJT4lDQogIG11dGF0ZSh0b3RhbF9kdXJhdGlvbiA9IHRvdGFsLnJpZGVzICogYXZnLmR1cmF0aW9uLCANCiAgICAgICAgIGhvdXIgPSBmYWN0b3IoaG91ciwgbGV2ZWxzID0gKDA6MjMpKSkgJT4lDQogIGdyb3VwX2J5KGhvdXIsIHdlZWtkYXkpICU+JQ0KICBzdW1tYXJpc2UoY291bnQucmlkZXMgPSBzdW0odG90YWwucmlkZXMpLCB0b3RhbC5kdXJhdGlvbiA9IHN1bSh0b3RhbF9kdXJhdGlvbikpIC0+IGRmLjENCmBgYA0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIENyZWF0ZSB0aW1lIGJhc2VkIGhlYXRtYXBzDQpnMTAwIDwtIGdncGxvdChkYXRhPWRmLjEsIGFlcyh4PWhvdXIsIHk9d2Vla2RheSwgZmlsbD1jb3VudC5yaWRlcykpICsNCiAgZ2VvbV90aWxlKGNvbG9yPSJ3aGl0ZSIsIHNpemU9MC4xKSsgY29vcmRfZXF1YWwoKSArDQogIGxhYnMoeD1OVUxMLCB5PU5VTEwsIHRpdGxlPSJDb3VudCBvZiBSaWRlcyBQZXIgV2Vla2RheSAmIEhvdXIgb2YgRGF5IikgKw0KICB0aGVtZV90dWZ0ZShiYXNlX2ZhbWlseT0iQ2FsaWJyaSIpICsgdGhlbWUocGxvdC50aXRsZT1lbGVtZW50X3RleHQoaGp1c3Q9MC41LCBzaXplID0gMTApKSArDQogIHRoZW1lKGF4aXMudGlja3M9ZWxlbWVudF9ibGFuaygpKSArIHRoZW1lKGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT03KSkgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gIndoaXRlIiwgaGlnaCA9ICJzdGVlbGJsdWUiKQ0KZzEwMSA8LSBnZ3Bsb3QoZGF0YT1kZi4xLCBhZXMoeD1ob3VyLCB5PXdlZWtkYXksIGZpbGw9dG90YWwuZHVyYXRpb24pKSArDQogIGdlb21fdGlsZShjb2xvcj0id2hpdGUiLCBzaXplPTAuMSkrIGNvb3JkX2VxdWFsKCkgKw0KICBsYWJzKHg9TlVMTCwgeT1OVUxMLCB0aXRsZT0iVG90YWwgRHVyYXRpb24gUGVyIFdlZWtkYXkgJiBIb3VyIG9mIERheSIpICsNCiAgdGhlbWVfdHVmdGUoYmFzZV9mYW1pbHk9IkNhbGlicmkiKSArIHRoZW1lKHBsb3QudGl0bGU9ZWxlbWVudF90ZXh0KGhqdXN0PTAuNSwgc2l6ZSA9IDEwKSkgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArDQogIHRoZW1lKGF4aXMudGlja3M9ZWxlbWVudF9ibGFuaygpKSArIHRoZW1lKGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT03KSkgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJ3aGl0ZSIsIGhpZ2ggPSAiZmlyZWJyaWNrIikNCmcxMDIgPC0gZ2dwbG90KGRhdGE9ZGYuMSwgYWVzKHg9aG91ciwgeT13ZWVrZGF5LCBmaWxsPXRvdGFsLmR1cmF0aW9uL2NvdW50LnJpZGVzKSkgKw0KICBnZW9tX3RpbGUoY29sb3I9IndoaXRlIiwgc2l6ZT0wLjEpKyBjb29yZF9lcXVhbCgpICsNCiAgbGFicyh4PU5VTEwsIHk9TlVMTCwgdGl0bGU9IkF2ZXJhZ2UgRHVyYXRpb24gUGVyIFdlZWtkYXkgJiBIb3VyIG9mIERheSIpICsNCiAgdGhlbWVfdHVmdGUoYmFzZV9mYW1pbHk9IkNhbGlicmkiKSArIHRoZW1lKHBsb3QudGl0bGU9ZWxlbWVudF90ZXh0KGhqdXN0PTAuNSwgc2l6ZSA9IDEwKSkgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArDQogIHRoZW1lKGF4aXMudGlja3M9ZWxlbWVudF9ibGFuaygpKSArIHRoZW1lKGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT03KSkgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJ3aGl0ZSIsIGhpZ2ggPSAic3ByaW5nZ3JlZW4zIikNCnBsb3RfZ3JpZChnMTAwLCBnMTAyLCBucm93ID0gMiwgcmVsX2hlaWdodHMgPSBjKDEvMiwgMS8yKSkNCmBgYA0KSGVyZSB3ZSBmaW5kIHNvbWUgaW50ZXJlc3RpbmcgcGF0dGVybnMgZnJvbSB0aGUgaG91ci13ZWVrZGF5IGhlYXRtYXAuIEl0IHNlZW1zIHRoYXQgbW9yZSByaWRlcyBoYXZlIHRha2VuIHBsYWNlIGR1cmluZyBydXNoIGhvdXJzIG9uIHdvcmsgZGF5cywgd2hpbGUgdG90YWwucmlkZXMgZGlzdHJpYnV0ZXMgZXZlbmx5IGluIGRheSB0aW1lIG9uIHdlZWtlbmQuIFRoZSBhdmcuZHVyYXRpb24gb2YgdGhlIHJpZGVzIGFwcGVhcnMgdG8gYmUgbG9uZ2VyIGR1cmluZyBkYXkgdGltZSBvdmVyIHRoZSB3ZWVrZW5kLg0KDQpBZnRlciB3ZSBoYXZlIGEgZ2VuZXJhbCB1bmRlcnN0YW5kaW5nIG9mIHRoZSBkYXRhLCB3ZSBtb3ZlIG9uIHRvIGV4cGxvcmUgdGhlIGdlb3NwYXRpYWwgZGlzdHJpYnV0aW9uIG9mIHRvdGFsLnJpZGVzIGFjcm9zcyB0aGUgREMgbWV0cm8gYXJlYS4gRmlyc3QgbGV0IHVzIHBsb3QgdGhlIGJpa2Ugc3RhdGlvbnMuDQpgYGB7cn0NCiMgQ3JlYXRlIHN0YXRpb24gbGlzdCB3aXRoIGNvb3JkaW5hdGVzLCB0b3RhbCBjb3VudCBvZiByaWRlcywgYW5kIHRvdGFsIGR1cmF0aW9uIG9mIHJpZGVzDQptYXAuc3RhdGlvbnMgPC0gbWFwX2RmICU+JQ0KICBncm91cF9ieShTdGFydC5zdGF0aW9uKSAlPiUNCiAgc3VtbWFyaXNlKHRvdGFsLnJpZGVzID0gbigpLA0KICAgICAgICAgICAgYXZnLmR1cmF0aW9uID0gbWVhbihkdXJhdGlvbi5taW4pLA0KICAgICAgICAgICAgc3Vic2NyaWJlci5wZXJjZW50YWdlID0gbWVhbihTdWJzY3JpcHRpb24uVHlwZSA9PSAiUmVnaXN0ZXJlZCIpLA0KICAgICAgICAgICAgbGF0ID0gZmlyc3QoTEFUSVRVREUpLA0KICAgICAgICAgICAgbG9uID0gZmlyc3QoTE9OR0lUVURFKQ0KICAgICAgICAgICAgKQ0KaGVhZChtYXAuc3RhdGlvbnMpDQpgYGANCkJlbG93IHdlIGNhbiBzZWUgdGhlIGxvY2F0aW9ucyBvZiBhbGwgdGhlIGJpa2Ugc2hhcmUgc3RhdGlvbnMgYWNyb3NzIHRoZSBETVYgYXJlYSwgd2l0aCB0aGUgY2lyY2xlIHNpemUgcmVwcmVzZW50aW5nIHRvdGFsLnJpZGVzIGFuZCBjb2xvciByZXByZXNlbnRpbmcgYXZnLnJpZGVzLiBJdCBhcHBlYXJzIHRoYXQgYmlrZSBzdGF0aW9ucyBhcmUgc3ByZWFkaW5nIG91dCB3ZWxsIGluIHRoZSBETVYgYXJlYSwgd2l0aCBzdGF0aW9ucyBsb2NhdGVkIGluIERNViBvdXJza2lydHMgc3VjaCBhcyBBbGV4YW5kcmlhLCBWQSwgQmV0aGVzZGEsIE1ELCBhbmQgU2lsdmVyIFNwcmluZywgTUQuDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgZG93bmxvYWQgYmFzaWMgbWFwIGxheWVycyBmb3IgcGxvdHRpbmcNCmJhc2UubWFwIDwtIHFtYXAoIldhc2luZ3RvbiBEQyIsIHpvb20gPSAxMiwgc291cmNlPSAiZ29vZ2xlIiwgbWFwdHlwZT0icm9hZG1hcCIsIGNvbG9yID0gImJ3IiwgY3JvcD1GQUxTRSwgbGVnZW5kPSd0b3BsZWZ0JykNCmJhc2UubWFwLjEgPC0gcW1hcCgiV2FzaW5ndG9uIERDIiwgem9vbSA9IDEzLCBzb3VyY2U9ICJnb29nbGUiLCBtYXB0eXBlPSJyb2FkbWFwIiwgY29sb3IgPSAiYnciLCBjcm9wPUZBTFNFLCBsZWdlbmQ9J3RvcGxlZnQnKQ0KYmFzZS5tYXAuMiA8LSBxbWFwKCJXYXNpbmd0b24gREMiLCB6b29tID0gMTQsIHNvdXJjZT0gImdvb2dsZSIsIG1hcHR5cGU9InJvYWRtYXAiLCBjb2xvciA9ICJidyIsIGNyb3A9RkFMU0UsIGxlZ2VuZD0ndG9wbGVmdCcpDQpgYGANCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTZ9DQpiYXNlLm1hcCArIGdlb21fcG9pbnQoYWVzKHggPSBsb24sIHkgPSBsYXQsIHNpemU9dG90YWwucmlkZXMsIGNvbG9yPWF2Zy5kdXJhdGlvbiksIGRhdGEgPSBtYXAuc3RhdGlvbnMsDQogYWxwaGEgPSAuNSkrIHNjYWxlX3NpemUocmFuZ2UgPSBjKDEsIDUpKSArIHNjYWxlX2NvbG91cl9ncmFkaWVudChsb3cgPSAicHVycGxlIiwgaGlnaCA9ICJyZWQiKQ0KYmFzZS5tYXAuMSArIGdlb21fcG9pbnQoYWVzKHggPSBsb24sIHkgPSBsYXQsIHNpemU9dG90YWwucmlkZXMsIGNvbG9yPWF2Zy5kdXJhdGlvbiksIGRhdGEgPSBtYXAuc3RhdGlvbnMsDQogYWxwaGEgPSAuNSkgKyBzY2FsZV9zaXplKHJhbmdlID0gYygxLCA1KSkgKyBzY2FsZV9jb2xvdXJfZ3JhZGllbnQobG93ID0gInB1cnBsZSIsIGhpZ2ggPSAicmVkIikNCmJhc2UubWFwLjIgKyBnZW9tX3BvaW50KGFlcyh4ID0gbG9uLCB5ID0gbGF0LCBzaXplPXRvdGFsLnJpZGVzLCBjb2xvcj1hdmcuZHVyYXRpb24pLCBkYXRhID0gbWFwLnN0YXRpb25zLA0KIGFscGhhID0gLjUpICsgc2NhbGVfc2l6ZShyYW5nZSA9IGMoMSwgMTApKSArIHNjYWxlX2NvbG91cl9ncmFkaWVudChsb3cgPSAicHVycGxlIiwgaGlnaCA9ICJyZWQiKQ0KDQpgYGANCkJ1dCBob3cgZG9lcyB0aGUgYWN0dWFsIGNvdW50IG9mIHRvdGFsLnJpZGVzIGRpc3RyaWJ1dGUgYWNyb3NzIHRoZSBhcmVhPyBXaWxsIGl0IGdvIGluIGxpbmUgd2l0aCB0aGUgYmlrZSBzdGF0aW9uIGxvY2F0aW9ucz8gV2UgdGhlbiBtb3ZlIG9uIHRvIGNyZWF0ZSBhIGhlYXRtYXAgYmFzZWQgb24gdGhlIGRlbnNpdHkgb2YgdG90YWwucmlkZXMgb24gdGhlIG1hcC4gVGhlIGdyYXBoIGJlbG93IGluZGljYXRlcyB0aGF0IHRvdGFsLnJpZGVzIGFyZSB3YXkgbW9yZSBjb25kZW5zZWQgdGhhbiB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBiaWtlIHN0YXRpb25zLCB3aXRoIHRoZSBtb3N0IHJpZGVzIGhhcHBlbmluZyBpbiB0aGUgREMgaGVhcnQgYXJlYSwgc3VjaCBhcyBEdXBvbnQgQ2lyY2xlLCBMb2dhbiBDaXJjbGUsIE5hdGlvbmFsIE1hbGwsIE1ldHJvIENlbnRlciwgR2FsbGVyeSBQbGFjZSwgV29ybGQgQmFuaywgYW5kIExpbmNvbG4gTWVtb3JpYWwuDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBDcmVhdGUgYSByaWRlIGRhdGEgc2V0IHdpdGggbG9jYXRpb24gYW5kIHJpZGUsIHdpbGwgYWxzbyBrZWVwIHNsaWNlYWJpbGl0eSB3aXRoIG90aGVyIGZhY3RvcnMNCiMgQWRqdXN0IGZhY3RvciBsZXZlbCBuYW1lcyBmb3IgYmV0dGVyIGRpc3BsYXkgaW4gZmFjZXRlZCB2aXN1YWxzDQptYXBfZGYgJT4lDQogIG11dGF0ZShsb24gPSBMT05HSVRVREUsIGxhdCA9IExBVElUVURFKSAlPiUNCiAgc2VsZWN0KFgsIFN1YnNjcmlwdGlvbi5UeXBlLCBFdmVudHMsIGxhdCwgbG9uLCBob3VyLA0KICAgICAgICAgd2Vla2RheSwgd2Vla2VuZCwgcnVzaGhvdXIsIGhvbGlkYXksIHNlYXNvbiwgQWR2ZXJzZVdlYXRoZXIsIEJlYXV0aWZ1bFdlYXRoZXIsIHdlZWtlbmRfaG9saWRheSkgJT4lDQogIG11dGF0ZSgNCiAgICBob3VyID0gYXMubnVtZXJpYyhob3VyKSwNCiAgICBBZHZlcnNlV2VhdGhlciA9IGFzLmZhY3RvcihpZl9lbHNlKEFkdmVyc2VXZWF0aGVyPT0iVHJ1ZSIsICJBZHZlcnNlOiBZZXMiLCAiQWR2ZXJzZTogTm8iKSksDQogICAgQmVhdXRpZnVsV2VhdGhlciA9IGFzLmZhY3RvcihpZl9lbHNlKEJlYXV0aWZ1bFdlYXRoZXIgPT0gIlRydWUiLCAiQmVhdXRpZnVsOiBZZXMiLCAiQmVhdXRpZnVsOiBObyIpKSwNCiAgICBob2xpZGF5ID0gYXMuZmFjdG9yKGlmX2Vsc2UoaG9saWRheSA9PSAiMSIsICJIb2xpZGF5OiBZZXMiLCAiSG9saWRheTogTm8iKSksDQogICAgd2Vla2VuZCA9IGFzLmZhY3RvcihpZl9lbHNlKHdlZWtlbmQgPT0gIjEiLCAiV2Vla2VuZDogWWVzIiwgIldlZWtlbmQ6IE5vIikpLA0KICAgIHJ1c2hob3VyID0gYXMuZmFjdG9yKGlmX2Vsc2UocnVzaGhvdXIgPT0gIjEiLCAiUnVzaCBIb3VyOiBZZXMiLCAiUnVzaCBIb3VyOiBObyIpKSwNCiAgICB3ZWVrZW5kX2hvbGlkYXkgPSBhcy5mYWN0b3IoaWZfZWxzZSh3ZWVrZW5kX2hvbGlkYXkgPT0gIjEiLCAiTGVpc3VyZSBEYXk6IFllcyIsICJMZWlzdXJlIERheTogTm8iKSksDQogICAgdGltZV9vZl9kYXkgPSBmYWN0b3IoaWZfZWxzZShob3VyPjQgJiBob3VyIDwgMTMsICJNb3JuaW5nIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmX2Vsc2UoaG91cj4xMiAmIGhvdXIgPCAxOSwgIkFmdGVybm9vbiIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZl9lbHNlKGhvdXIgPjE2ICYgaG91ciA8PSAyMywgIk5pZ2h0IiwgIkxhdGUgTmlnaHQiKSkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIk1vcm5pbmciLCAiQWZ0ZXJub29uIiwgIk5pZ2h0IiwgIkxhdGUgTmlnaHQiKSksDQogICAgaG91ciA9IGZhY3Rvcihob3VyLCBsZXZlbHMgPSAwOjIzKSkgLT4gcmlkZV9kZg0KYGBgDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9Nn0NCiMgQ3JlYXRlIHJpZGUgZGVuc2l0eSBtYXBzDQpiYXNlLm1hcCArIGdlb21fZGVuc2l0eTJkKGRhdGEgPSByaWRlX2RmW3NhbXBsZSgxOm5yb3cocmlkZV9kZiksIDEwMDAwKSxdLCANCiAgICBhZXMoeCA9IGxvbiwgeSA9IGxhdCksIHNpemUgPSAwLjQpICsgc3RhdF9kZW5zaXR5MmQoZGF0YSA9IHJpZGVfZGZbc2FtcGxlKDE6bnJvdyhyaWRlX2RmKSwgMTAwMDApLF0sIA0KICAgIGFlcyh4ID0gbG9uLCB5ID0gbGF0LCBmaWxsID0gLi5sZXZlbC4uLCBhbHBoYSA9IC4ubGV2ZWwuLiksIHNpemUgPSAxLCANCiAgICBiaW5zID0gNSwgZ2VvbSA9ICJwb2x5Z29uIiwgY29udG91ciA9IFRSVUUpICsgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAic3ByaW5nZ3JlZW4iLCBoaWdoID0gInJlZCIpICsgDQogICAgc2NhbGVfYWxwaGEocmFuZ2UgPSBjKDAsIDAuMyksIGd1aWRlID0gRkFMU0UpDQoNCmJhc2UubWFwLjEgKyBnZW9tX2RlbnNpdHkyZChkYXRhID0gcmlkZV9kZltzYW1wbGUoMTpucm93KHJpZGVfZGYpLCAxMDAwMCksXSwgDQogICAgYWVzKHggPSBsb24sIHkgPSBsYXQpLCBzaXplID0gMC40KSArIHN0YXRfZGVuc2l0eTJkKGRhdGEgPSByaWRlX2RmW3NhbXBsZSgxOm5yb3cocmlkZV9kZiksIDEwMDAwKSxdLCANCiAgICBhZXMoeCA9IGxvbiwgeSA9IGxhdCwgZmlsbCA9IC4ubGV2ZWwuLiwgYWxwaGEgPSAuLmxldmVsLi4pLCBzaXplID0gMiwgDQogICAgYmlucyA9IDgsIGdlb20gPSAicG9seWdvbiIsIGNvbnRvdXIgPSBUUlVFKSArIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gInNwcmluZ2dyZWVuIiwgaGlnaCA9ICJyZWQiKSArIA0KICAgIHNjYWxlX2FscGhhKHJhbmdlID0gYygwLCAwLjMpLCBndWlkZSA9IEZBTFNFKQ0KDQpiYXNlLm1hcC4yICsgZ2VvbV9kZW5zaXR5MmQoZGF0YSA9IHJpZGVfZGZbc2FtcGxlKDE6bnJvdyhyaWRlX2RmKSwgMTAwMDApLF0sIA0KICAgIGFlcyh4ID0gbG9uLCB5ID0gbGF0KSwgc2l6ZSA9IDAuNSkgKyBzdGF0X2RlbnNpdHkyZChkYXRhID0gcmlkZV9kZltzYW1wbGUoMTpucm93KHJpZGVfZGYpLCAxMDAwMCksXSwgDQogICAgYWVzKHggPSBsb24sIHkgPSBsYXQsIGZpbGwgPSAuLmxldmVsLi4sIGFscGhhID0gLi5sZXZlbC4uKSwgc2l6ZSA9IDMsIA0KICAgIGJpbnMgPSAxNSwgZ2VvbSA9ICJwb2x5Z29uIiwgY29udG91ciA9IFRSVUUpICsgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAic3ByaW5nZ3JlZW4iLCBoaWdoID0gInJlZCIpICsgDQogICAgc2NhbGVfYWxwaGEocmFuZ2UgPSBjKDAsIDAuMyksIGd1aWRlID0gRkFMU0UpDQpgYGANClNpbmNlIHdlIG5vdyBoYXZlIGEgZ2VuZXJhbCBpZGVhIG9mIHdoZXJlIHRoZSBtb3N0IHJpZGVzIGFyZSBoYXBwZW5pbmcgaW4gREMsIG91ciBuZXh0IHN0ZXAgaXMgdG8gc2xpY2UgdGhlIHJpZGVyc2hpcCBkYXRhIHdpdGggZmFjdG9ycyB3ZSBnZW5lcmF0ZWQgZnJvbSB0aW1lIGFuZCB3ZWF0aGVyIGFuZCBjb21wYXJlIHRoZSBwYXR0ZXJucy4gV2Ugd2FudGVkIHRvIHNlZSBpZiB0aGUgcG9wdWxhcml0eSBvZiB0aGUgc3RhdGlvbnMgY2hhbmdlZCB1bmRlciBkaWZmZXJlbnQgdGltZSBhbmQgd2VhdGhlciBjb25kaXRpb25zLg0KYGBge3J9DQojIENyZWF0ZSBhIHN1YnNsaWNlZCByaWRlcnNoaXAgc2V0IG9mIDE1MDAwIG9ic2VydmF0aW9ucw0KcmlkZV9kZi5zYW1wbGUgPC0gcmlkZV9kZltzYW1wbGUoMTpucm93KHJpZGVfZGYpLCAxNTAwMCksXQ0KYGBgDQpPdXIgbmV4dCBzdGVwIGlzIHRvIHNsaWNlIHRoZSByaWRlcnNoaXAgZGF0YSBhY2NvcmluZGcgdG8gZmFjdG9ycyB3ZSBnZW5lcmF0ZWQgZnJvbSB3ZWF0aGVyIGFuZCB0aW1lLiBXZSB3YW50ZWQgdG8gc2VlIGlmIHRoZSBwb3B1bGFyaXR5IG9mIHRoZSBzdGF0aW9ucyBjaGFuZ2VkIHVuZGVyIGRpZmZlcmVudCB0aW1lIGFuZCB3ZWF0aGVyIGNvbmRpdGlvbnMuDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCiMgQ3JlYXRlIGJhc2UgbGF5ZXJzIGZvciBmYWNldGVkIG1hcHBpbmcNCmRjLjEgPC0gZ2V0X21hcCgnd2FzaGluZ3RvbiBkYycsIHpvb20gPSAxMiwgc291cmNlID0gImdvb2dsZSIsIG1hcHR5cGUgPSAicm9hZG1hcCIsIGNyb3A9RkFMU0UsIGNvbG9yPSJidyIpDQpkYy4yIDwtIGdldF9tYXAoJ3dhc2hpbmd0b24gZGMnLCB6b29tID0gMTMsIHNvdXJjZSA9ICJnb29nbGUiLCBtYXB0eXBlID0gInJvYWRtYXAiLCBjcm9wPUZBTFNFLCBjb2xvcj0iYnciKQ0KZGMuMyA8LSBnZXRfbWFwKCd3YXNoaW5ndG9uIGRjJywgem9vbSA9IDE0LCBzb3VyY2UgPSAiZ29vZ2xlIiwgbWFwdHlwZSA9ICJyb2FkbWFwIiwgY3JvcD1GQUxTRSwgY29sb3I9ImJ3IikNCmRjLm1hcC4xIDwtIGdnbWFwKGRjLjEsIGJhc2VfbGF5ZXIgPSBnZ3Bsb3QoYWVzKHggPSBsb24sIHkgPSBsYXQpLCBkYXRhID0gcmlkZV9kZi5zYW1wbGUpKQ0KZGMubWFwLjIgPC0gZ2dtYXAoZGMuMiwgYmFzZV9sYXllciA9IGdncGxvdChhZXMoeCA9IGxvbiwgeSA9IGxhdCksIGRhdGEgPSByaWRlX2RmLnNhbXBsZSkpDQpkYy5tYXAuMyA8LSBnZ21hcChkYy4zLCBiYXNlX2xheWVyID0gZ2dwbG90KGFlcyh4ID0gbG9uLCB5ID0gbGF0KSwgZGF0YSA9IHJpZGVfZGYuc2FtcGxlKSkNCmBgYA0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTl9DQojIFJpZGUgZnJlcXVlbmN5IGhlYXRtYXAgYnkgc2Vhc29ucw0KZGMubWFwLjMgKyBzdGF0X2RlbnNpdHkyZChhZXMoeD1sb24sIHk9bGF0LCBmaWxsPS4ubGV2ZWwuLiwgYWxwaGE9Li5sZXZlbC4uKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgYmlucz03LCBnZW9tPSJwb2x5Z29uIiwgZGF0YT1yaWRlX2RmLnNhbXBsZSkgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdz0ic3ByaW5nZ3JlZW4iLCBoaWdoPSJ0b21hdG8iKSArIHNjYWxlX2FscGhhKHJhbmdlID0gYygwLjEsIDAuNiksIGd1aWRlID0gRkFMU0UpICsgDQogIGZhY2V0X3dyYXAofnNlYXNvbiwgbnJvdyA9IDEpICsNCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKHRpdGxlPSJyaWRlXG5mcmVxdWVuY3kiKSkgKw0KICBnZ3RpdGxlKCJSaWRlIERpc3RyaWJ1dGlvbiBieSBTZWFzb25zIikgKw0KICB0aGVtZShheGlzLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50aWNrcz1lbGVtZW50X2JsYW5rKCksDQogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGNvbG9yPSJibGFjayIsIHNpemU9MTYsIGhqdXN0PTApKSAtPiBnMTMNCmcxMw0KYGBgDQoNClRoZSBmaXJzdCBncmFwaCBzaG93cyB0aGUgZGlzdHJpYnV0aW9uIG9mIHJpZGVzIGluIGVhY2ggc2Vhc29uIG9mIHRoZSB5ZWFyIG9mIDIwMTUuIEluIFNwcmluZyBhbmQgU3VtbWVyLCBib3RoIExpbmNvbG4gTWVtb3JpYWwgYW5kIE5hdGlvbmFsIE1hbGwgZW5qb3kgbW9yZSByaWRlcyBmcm9tIG90aGVyIHRpbWUgb2YgdGhlIHllYXIuIER1cmluZyB3aW50ZXIsIGhvd2V2ZXIsIGl0IHNlZW1zIHRoYXQgbW9yZSBwZW9wbGUgYXJlIHRha2luZyBiaWtlIHJpZGVzIGFyb3VuZCBMb2dhbiBDaXJjbGUsIEZvZ2d5IEJvdHRvbSwgYW5kIE1ldHJvIENlbnRlciwgaS5lLiB0aGUgaW5uZXIgY2VudGVyIG9mIHRoZSBEaXN0cmljdC4NCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTl9DQojIFJpZGUgZnJlcXVlbmN5IGhlYXRtYXAgYnkgdGltZSBvZiBkYXkNCmRjLm1hcC4zICsgc3RhdF9kZW5zaXR5MmQoYWVzKHg9bG9uLCB5PWxhdCwgZmlsbD0uLmxldmVsLi4sIGFscGhhPS4ubGV2ZWwuLiksDQogICAgICAgICAgICAgICAgICAgICAgICAgIGJpbnM9NywgZ2VvbT0icG9seWdvbiIsIGRhdGE9cmlkZV9kZi5zYW1wbGUpICsNCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3c9InNwcmluZ2dyZWVuIiwgaGlnaD0idG9tYXRvIikgKyBzY2FsZV9hbHBoYShyYW5nZSA9IGMoMC4xLCAwLjYpLCBndWlkZSA9IEZBTFNFKSArIA0KICBmYWNldF93cmFwKH50aW1lX29mX2RheSwgbnJvdyA9IDEpICsNCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKHRpdGxlPSJyaWRlXG5mcmVxdWVuY3kiKSkgKw0KICBnZ3RpdGxlKCJSaWRlIERpc3RyaWJ1dGlvbiBieSBUaW1lIG9mIERheSIpICsNCiAgdGhlbWUoYXhpcy50aXRsZT1lbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGV4dD1lbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGlja3M9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChjb2xvcj0iYmxhY2siLCBzaXplPTE2LCBoanVzdD0wKSkgLT4gZzE0DQpnMTQNCmBgYA0KDQpBbm90aGVyIHNpbWlsYXIgY29tcGFyaXNvbiBiYXNlZCBvbiB0aW1lIG9mIHRoZSBkYXkgc2hvd3MgdGhhdCBwZW9wbGUgYXJlIHRha2luZyBtb3JlIHJpZGVzIGluIGNlbnRyYWwgdG8gbm9ydGhlYXN0ZXJuIERDIGluIHRoZSBtb3JuaW5nIGFuZCBtb3JlIGluIGNlbnRyYWwgdG8gc291dGh3ZXN0ZXJuIERDIGluIHRoZSBhZnRlcm5vb24uIEJpa2VycyBzdGFydCB0aGVpciByaWRlcyBtb3N0bHkgYXJvdW5kIER1UG9udCBjaXJjbGUsIExvZ2FuIENpcmNsZSwgTWV0cm8gQ2VudGVyLCBhbmQgR2FsbGVyeSBQbGFjZSBhdCBuaWdodC4gRmV3IHBlb3BsZSB3aWxsIHN0YXJ0IHRoZWlyIHJpZGVzIGluIGxhdGUgbmlnaHQsIG9mIGNvdXJzZTsgYnV0IHdlIGFyZSBzZWVpbmcgcmVsYXRpdmVseSBtb3JlIHJpZGVzIGluIHRoZSBjZW50cmFsIHRvIG5vcnRod2VzdGVybiBEQyBhcmVhLiBJdCBzZWVtcyB0aGF0IHBlb3BsZSdzIGRhaWx5IHJvdXRpbmUgaXMgY29udHJpYnV0aW5nIHRvIHRoaXMgcGF0dGVybiwgY29uc2lkZXJpbmcgdGhhdCB0aGVzZSBhcmVhcyBjb3JyZXNwb25kIHRvIHRoZSByZXNpZGVuY2UgYXJlYSwgd29ya2luZyBhcmVhLCBhbmQgZW50ZXJ0YWluaW5nL2V2ZW50IGFyZWEgaW4gREMuDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD04fQ0KIyBSaWRlIGZyZXF1ZW5jeSBoZWF0bWFwIGJ5IHJ1c2ggaG91cg0KZGMubWFwLjMgKyBzdGF0X2RlbnNpdHkyZChhZXMoeD1sb24sIHk9bGF0LCBmaWxsPS4ubGV2ZWwuLiwgYWxwaGE9Li5sZXZlbC4uKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgYmlucz03LCBnZW9tPSJwb2x5Z29uIiwgZGF0YT1yaWRlX2RmLnNhbXBsZSkgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdz0ic3ByaW5nZ3JlZW4iLCBoaWdoPSJ0b21hdG8iKSArIHNjYWxlX2FscGhhKHJhbmdlID0gYygwLjEsIDAuNiksIGd1aWRlID0gRkFMU0UpICsgDQogIGZhY2V0X3dyYXAofnJ1c2hob3VyKSArDQogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZCh0aXRsZT0icmlkZVxuZnJlcXVlbmN5IikpICsNCiAgZ2d0aXRsZSgiUmlkZSBEaXN0cmlidXRpb24gLSBSdXNoIEhvdXI/IikgKw0KICB0aGVtZShheGlzLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50aWNrcz1lbGVtZW50X2JsYW5rKCksDQogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGNvbG9yPSJibGFjayIsIHNpemU9MTYsIGhqdXN0PTApKSAtPiBnMTUNCmcxNQ0KYGBgDQoNClNpbmNlIHRpbWUgaXMgY3JlYXRpbmcgaW50ZXJlc3RpbmcgaW1wYWN0IG9uIHRvdGFsLnJpZGVzIGFuZCBiaWtlcyBjYW4gYmUgYSB1c2VmdWwgdG9vbCBmb3IgY29tbXV0aW5nLCB3ZSB3YW50IHRvIGNoZWNrIG91dCBzcGVjaWZpY2FsbHkgdGhlIGFsbG9jYXRpb24gb2YgcmlkZXMgZm9yIHJ1c2ggaG91cnMgYWdhaW50IG90aGVyIHRpbWUgb2YgdGhlIGRheS4gSW4gdGhlIGFib3ZlIGdyYXBoLCB3ZSBub3RpY2UgdGhhdCBtb3JlIHBlb3BsZSBhcmUgdGFraW5nIGJpa2UgcmlkZXMgbmVhciBNZXRybyBDZW50ZXIsIEdhbGxlcnkgUGxhY2UsIGFuZCBDYXBpdGFsIEhpbGwgZHVyaW5nIHJ1c2ggaG91cnMsIHdoaWxlIG1vcmUgcGVvcGxlIGFyZSB0YWtpbmcgcmlkZXMgbmVhciBMaW5jb2xuIE1lbW9yaWFsIGFuZCBOYXRpb25hbCBNYWxsIGR1cmluZyBub24tcnVzaCBob3Vycy4gVGhpcyBpbmZvcm1hdGlvbiBpcyBpbnRlcmVzdGluZywgc2luY2UgTWV0cm8gY2VudGVyLCBHYWxsZXJ5IHBsYWNlLCBhbmQgQ2FwaXRhbCBIaWxsIGFyZSBwbGFjZXMgd2hlcmUgbWFueSBwZW9wbGUgZ28gdG8gd29yaywgd2hpbGUgKGFwcGFyZW50bHkpIExpbmNvbG4gTWVtb3JpYWwgYW5kIE5hdGlvbmFsIE1hbGwgYXJlIHBvcHVsYXIgdG91cmlzdCBzaXRlcy4NCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTl9DQojIFJpZGUgZnJlcXVlbmN5IGhlYXRtYXAgYnkgd2Vla2VuZC9ob2xpZGF5DQpkYy5tYXAuMyArIHN0YXRfZGVuc2l0eTJkKGFlcyh4PWxvbiwgeT1sYXQsIGZpbGw9Li5sZXZlbC4uLCBhbHBoYT0uLmxldmVsLi4pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBiaW5zPTcsIGdlb209InBvbHlnb24iLCBkYXRhPXJpZGVfZGYuc2FtcGxlKSArDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93PSJzcHJpbmdncmVlbiIsIGhpZ2g9InRvbWF0byIpICsgc2NhbGVfYWxwaGEocmFuZ2UgPSBjKDAuMSwgMC42KSwgZ3VpZGUgPSBGQUxTRSkgKyANCiAgZmFjZXRfd3JhcCh+d2Vla2VuZF9ob2xpZGF5ICsgQmVhdXRpZnVsV2VhdGhlciwgbnJvdyA9IDEpICsNCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKHRpdGxlPSJyaWRlXG5mcmVxdWVuY3kiKSkgKw0KICBnZ3RpdGxlKCJSaWRlIERpc3RyaWJ1dGlvbiAtIExlaXN1cmUgRGF5cyBYIEdvb2QgV2VhdGhlciIpICsNCiAgdGhlbWUoYXhpcy50aXRsZT1lbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGV4dD1lbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGlja3M9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChjb2xvcj0iYmxhY2siLCBzaXplPTE2LCBoanVzdD0wKSkgLT4gZzE2DQpnMTYNCmBgYA0KU2luY2UgTGluY29sbiBNZW1vcmlhbCBhbmQgTmF0aW9uYWwgTWFsbCBhcmUgZW5qb3lpbmcgbXVjaCBsb3ZlIGluIG5vbi1ydXNoIGhvdXJzLCB3ZSBhcmUgaW50ZXJlc3RlZCB0byBjaGVjayBvdXQgaWYgbGVpc3VyZSB0aW1lIHdpbGwgaGF2ZSBhIGRpZmZlcmVudCBwYXR0ZXJuIGZvciB0b3RhbC5yaWRlcyBkaXN0cmlidXRpb24uIENvbXBhcmluZyB0aGUgbGVmdCB0d28gZ3JhcGhzIGluIHRoZSBhYm92ZSBjaGFydCwgaXQgaXMgYXBwYXJlbnQgdGhhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIHJpZGVyc2hpcCBpcyBzcGFyc2UgZm9yIGxlaXN1cmUgZGF5cyBpbiBnb29kIHdlYXRoZXI6IHJpZGVycyBhcmUgb2YgY291cnNlIHN0YXJ0aW5nIHRoZWlyIHJpZGVzIGZyb20gbWFueSBkaWZmZXJlbnQgc3RhdGlvbnMgYWNyb3NzIHRoZSBEaXN0cmljdC4gSW50ZXJlc3RpbmdseSwgdGhlIHNlY29uZCBsZWZ0IGdyYXBoIHNob3dzIHRoYXQgYmlrZXJzIG1vc3RseSBzdGlsbCByaWRlIGluIHRoZSBjZW50cmFsIERDIGR1cmluZyB3b3JraW5nIGRheXMgZGVzcGl0ZSB0aGUgZ29vZCB3ZWF0aGVyLiBDb21tdXRpbmcgcmVhbGx5IHNlZW1zIHRvIGJlIGEgbWFqb3IgZnVuY3Rpb24gb2YgdGhlIHNoYXJlZCBiaWtlcyENCg0KU2luY2UgY29tbXV0aW5nIHNlZW1zIHRvIGJlIGEgcmVhbGx5IGJpZyBmYWN0b3IgZm9yIHRoZSBkaXN0cmlidXRpb24gb2YgcmlkZXMsIHdlIGFyZSBpbnN0ZXJlc3RlZCB0byBkaWcgYSBiaXQgZGVlcGVyIGludG8gdGhlIHR5cGUgb2Ygc3Vic2NyaXB0aW9uIGZvciBlYWNoIHJpZGUuIFNpbmNlIGJpa2Ugc2hhcmUgc3Vic2NyaWJlcnMgYXJlIG1vcmUgbGlrZWx5IHRvIHVzZSBiaWtlcyBmb3IgY29tbXV0ZSwgd2lsbCB3ZSBzZWUgYSBjbGVhciBkaWZmZXJlbmNlIGJldHdlZW4gY2FzdWFsIGFuZCByZWdpc3RlcmVkIGJpa2Vycz8NCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTl9DQojIFJpZGUgZnJlcXVlbmN5IGhlYXRtYXAgYnkgU3Vic2NyaXB0aW9uIFR5cGUNCmRjLm1hcC4yICsgc3RhdF9kZW5zaXR5MmQoYWVzKHg9bG9uLCB5PWxhdCwgZmlsbD0uLmxldmVsLi4sIGFscGhhPS4ubGV2ZWwuLiksDQogICAgICAgICAgICAgICAgICAgICAgICAgIGJpbnM9NywgZ2VvbT0icG9seWdvbiIsIGRhdGE9cmlkZV9kZi5zYW1wbGUpICsNCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3c9InNwcmluZ2dyZWVuIiwgaGlnaD0idG9tYXRvIikgKyBzY2FsZV9hbHBoYShyYW5nZSA9IGMoMC4xLCAwLjYpLCBndWlkZSA9IEZBTFNFKSArIA0KICBmYWNldF93cmFwKH5TdWJzY3JpcHRpb24uVHlwZSArIHJ1c2hob3VyLCBucm93ID0gMSkgKw0KICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQodGl0bGU9InJpZGVcbmZyZXF1ZW5jeSIpKSArDQogIGdndGl0bGUoIlJpZGUgRGlzdHJpYnV0aW9uIGJ5IFN1YnNjcmlwdGlvbiBUeXBlICYgUnVzaCBIb3VyIikgKw0KICB0aGVtZShheGlzLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50aWNrcz1lbGVtZW50X2JsYW5rKCksDQogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGNvbG9yPSJibGFjayIsIHNpemU9MTYsIGhqdXN0PTApKSAtPiBnMTcNCmcxNw0KYGBgDQoNClRoZSBhYm92ZSBncmFwaCBzaG93cyB0aGF0IGNhc3VhbCBiaWtlcnMgYXJlIChhcHBhcmVudGx5KSB0YWtpbmcgbW9yZSByaWRlcyBhcm91bmQgdGhlIHRvdXJpc3QgYXR0cmFjdGlvbiBzaXRlcyBpbiBEQywgbm8gbWF0dGVyIGlmIGl0J3MgaW4gcnVzaCBob3VyIG9yIG5vdC4gRm9yIHRoZSBzdWJzY3JpYmVycywgaG93ZXZlciwgdGhlIGRpc3RyaWJ1dGlvbiBvZiByaWRlcyBhcmUgc3VycHJpc2luZ2x5IGV2ZW4gbm8gbWF0dGVyIGl0J3MgcnVzaCBob3VyIG9yIG5vdC4gSWYgd2UgcmVhbGx5IGNvbnNpZGVyIHRoZSBuYXR1cmUgb2YgY29tbXV0aW5nLCB0aGlzIGFjdHVhbGx5IG1ha2VzIHNlbnNlOiBmb3IgcGVvcGxlIHRoYXQgcmlkZSBiaWtlcyBiYXNlZCBvbiB0aGVpciBkYWlseSBjb21tdXRpbmcgbmVlZHMsIHRoZXkgd2lsbCBuZWVkIHRvIHVzZSBiaWtlcyB0byBnZXQgdG8gd29yayBvciBnbyBob21lLiBUaGUgZ3JlZW4gYXJlYSBpbiB0aGUgcmlnaHQgdHdvIGdyYXBocyBhY3R1YWxseSBzaG93cyB0aGUgcm91dGluZSBzdGFydCBzdGF0aW9ucyBmb3IgdGhlIHJlZ2lzdGVyZWQgdXNlcnMhDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD04fQ0KIyBSaWRlIGZyZXF1ZW5jeSBoZWF0bWFwIGJ5IGFkdmVyc2Ugd2VhdGhlcg0KZGMubWFwLjIgKyBzdGF0X2RlbnNpdHkyZChhZXMoeD1sb24sIHk9bGF0LCBmaWxsPS4ubGV2ZWwuLiwgYWxwaGE9Li5sZXZlbC4uKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgYmlucz03LCBnZW9tPSJwb2x5Z29uIiwgZGF0YT1yaWRlX2RmLnNhbXBsZSkgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdz0ic3ByaW5nZ3JlZW4iLCBoaWdoPSJ0b21hdG8iKSArIHNjYWxlX2FscGhhKHJhbmdlID0gYygwLjEsIDAuNiksIGd1aWRlID0gRkFMU0UpICsgDQogIGZhY2V0X3dyYXAofkFkdmVyc2VXZWF0aGVyKSArDQogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZCh0aXRsZT0icmlkZVxuZnJlcXVlbmN5IikpICsNCiAgZ2d0aXRsZSgiUmlkZSBEaXN0cmlidXRpb24gLSBBZHZlcnNlIFdlYXRoZXI/IikgKw0KICB0aGVtZShheGlzLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50aWNrcz1lbGVtZW50X2JsYW5rKCksDQogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGNvbG9yPSJibGFjayIsIHNpemU9MTYsIGhqdXN0PTApKSAtPiBnMTgNCmcxOA0KYGBgDQoNCkEgcXVpY2sgY29tcGFyaXNvbiBvZiBhZHZlcnNlIHdlYXRoZXIgYWdhaW5zdCBub24tYWR2ZXJzZSB3ZWF0aGVyIHNob3dzIG5vdCBtdWNoIGRpZmZlcmVuY2UgZm9yIHRoZSByaWRlcnNoaXAuIFRoaXMgbWlnaHQgYmUgZHVlIHRvIHRoZSBuYXR1cmUgb2Ygb3VyIGludGVncmF0ZWQgd2VhdGhlciBkYXRhOiB0aGUgd2VhdGhlciBpbmZvcm1hdGlvbiBpcyB0aGUgbWVhbiB2YWx1ZXMgZm9yIGEgd2hvbGUgZGF5LCB0aHVzIG1ha2luZyBpdCBoYXJkIGZvciB0aGUgc2xpY2VycyB0byBkaWZmZXJlbnRpYXRlIHJpZGVyc2hpcCBkaXN0cmlidXRpb24gb24gYSBsb3dlciBncmFpbiBsZXZlbC4gDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9OX0NCiMgUmlkZSBmcmVxdWVuY3kgaGVhdG1hcCBieSBydXNoIGhvdXIgYW5kIGFkdmVyc2Ugd2VhdGhlcg0KZGMubWFwLjMgKyBzdGF0X2RlbnNpdHkyZChhZXMoeD1sb24sIHk9bGF0LCBmaWxsPS4ubGV2ZWwuLiwgYWxwaGE9Li5sZXZlbC4uKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgYmlucz03LCBnZW9tPSJwb2x5Z29uIiwgZGF0YT1yaWRlX2RmLnNhbXBsZSkgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdz0ic3ByaW5nZ3JlZW4iLCBoaWdoPSJ0b21hdG8iKSArIHNjYWxlX2FscGhhKHJhbmdlID0gYygwLjEsIDAuNiksIGd1aWRlID0gRkFMU0UpICsgDQogIGZhY2V0X3dyYXAofkFkdmVyc2VXZWF0aGVyICsgcnVzaGhvdXIsIG5yb3cgPSAxKSArDQogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZCh0aXRsZT0icmlkZVxuZnJlcXVlbmN5IikpICsNCiAgZ2d0aXRsZSgiUmlkZSBEaXN0cmlidXRpb24gLSBCYWQgV2VhdGhlciBYIFJ1c2ggSG91ciIpICsNCiAgdGhlbWUoYXhpcy50aXRsZT1lbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGV4dD1lbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGlja3M9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChjb2xvcj0iYmxhY2siLCBzaXplPTE2LCBoanVzdD0wKSkgLT4gZzE5DQpnMTkNCmBgYA0KQWdhaW4sIGluIHRoZSBncmFwaCBzaG93biBhYm92ZSBoZXJlLCB3ZSBvYnNlcnZlIGEgYmlnZ2VyIGRpZmZlcmVjZSBmcm9tIFJ1c2ggSG91ciB0aGFuIHRoZSB3ZWF0aGVyLiBUaGlzIHNlZW1zIHRvIGJlIHJlbGF0ZWQgdG8gdGhlIHNhbWUgY2hhbGxlbmdlIHdlIGFyZSBoYXZpbmcgZnJvbSB0aGUgd2VhdGhlciB2YXJpYWJsZXMuIA0KDQo=